package com.lan.shiro;

import com.lan.SysConstants;
import com.lan.businessObject.LoginUser;
import com.lan.entity.sys.Menu;
import com.lan.entity.sys.Role;
import com.lan.entity.sys.User;
import com.lan.enums.sys.MenuTypeEnum;
import com.lan.service.sys.MenuService;
import com.lan.service.sys.RoleService;
import com.lan.service.sys.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.BeanUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

public class CustomRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menusService;

    /**
     * 认证.
     * SecurityUtils.getSubject().login(token)
     *
     * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken authenticationToken) throws AuthenticationException {
        LoginUser loginUser = new LoginUser();
        String account = (String) authenticationToken.getPrincipal();
        if (null == account) {
            return null;
        }
        User user = userService.getUserByAccount(account);
        if (null != user) {
            BeanUtils.copyProperties(user, loginUser);
            // --------------------  user -> role list  -------------------- //
            List<Role> roleList = roleService.getRoleListByUserId(user.getId());
            // --------------------  role -> menu list  -------------------- //
            Set<String> roles = new HashSet<>();       //角色set
            Set<String> permissions = new HashSet<>(); //权限set
            Set<Menu> menuSet = new HashSet<>();       //菜单set
            Optional.ofNullable(roleList)
                    .filter(CollectionUtils::isNotEmpty)
                    .ifPresent(list -> list.forEach(role -> {
                        roles.add(role.getCode());     //添加角色
                        Optional.ofNullable(menusService.getMenuListByRoleId(role.getId()))
                                .filter(CollectionUtils::isNotEmpty)
                                .ifPresent(menuSet::addAll);
                    }));
            //对菜单列表分级
            Map<String, List<Menu>> map = new HashMap<>();
            Optional.of(menuSet.stream().sorted(Comparator.comparing(Menu::getOrderNum)).collect(Collectors.toList()))
                    .filter(CollectionUtils::isNotEmpty)
                    .ifPresent(list -> list.forEach(menu -> {
                        Optional.ofNullable(menu.getPermission()) //添加权限
                                .filter(StringUtils::isNotEmpty)
                                .ifPresent(permissions::add);
                        if (menu.getType().equals(MenuTypeEnum.FOLDER.getValue()+"") ||
                                menu.getType().equals(MenuTypeEnum.MENU.getValue()+"")) {
                            String parent = menu.getParentId();
                            if (null != parent && map.containsKey(parent))
                                map.get(parent).add(menu);
                            else {
                                List<Menu> childrenList = new ArrayList<>();
                                childrenList.add(menu);
                                map.put(parent, childrenList);
                            }
                        }
                    }));
            List<Menu> top = map.get(SysConstants.PARENT_ID);
            map.forEach((key, value) -> {
                if (!StringUtils.equals(key, SysConstants.PARENT_ID)) {
                    top.forEach(x -> {
                        if (x.getId().equals(key))
                            x.setChildren(value);
                    });
                }
            });
            loginUser.setRoles(roles);
            loginUser.setPermissions(permissions);
            loginUser.setMenuList(top);
            return new SimpleAuthenticationInfo(loginUser, loginUser.getPassword(), getName());
        } else {
            return null;
        }
    }

    /**
     * 授权.
     * 授权 ==> 执行isPermitted()或注解( @RequiresPermissions()/@RequiresRoles() )进入
     * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        Optional.ofNullable(loginUser).ifPresent(x -> {
            info.setRoles(loginUser.getRoles());
            info.addStringPermissions(loginUser.getPermissions());
        });
        return info;
    }
}
